/**
* @file app_light_control.c
* @brief Common process - adapter the flash api
* @version 0.1
* @date 2021-10-22
*
* @copyright Copyright 2020-2021 Tuya Inc. All Rights Reserved.
*
*/

#include "tkl_network_manager.h"
#include "tal_log.h"
#include "tal_memory.h"
#include "tal_system.h"
#include "tal_flash.h"
#include "tal_watchdog.h"
#include "tal_nv_flash.h"
#include "tal_sw_timer.h"
#include "tal_attribute_rw.h"
#include "app_light_control.h"
#include "app_common.h"
#include "app_cluster_level.h"
#include "app_cluster_color_control.h"
#include "app_cluster_private_tuya.h"
#include "app_handle.h"
#include "ty_scene.h"

STATIC LIGHT_CTRL_DATA_T sg_light_ctrl_data;
STATIC UINT8_T more_rst = FALSE;

extern TIMER_ID etimer_light_shade;
extern TIMER_ID etimer_save_data_delay;
extern TIMER_ID etimer_mf_blink;
extern TIMER_ID etimer_power_on_sync;
extern TIMER_ID etimer_join_end_sync;


/**
 * @note: light ctrl blink proc
 * @param {none}
 * @return: UCHAR_T
 */
VOID_T app_light_ctrl_blink_display(LIGHT_DATA_T *data)
{
    STATIC UCHAR_T cnt = 0;
    UINT16_T bright = (cnt % 2) ? DISTRIBUTION_REMINDER_BRIGHT_VALUE : 0;
    tal_system_memset(data, 0, SIZEOF(LIGHT_DATA_T));
    app_light_tools_BT2CW(bright, DISTRIBUTION_REMINDER_TEMPER_VALUE, data);
    if (sg_light_ctrl_data.blinkTime100ms & 0x8000)
    {
        sg_light_ctrl_data.blinkTime100ms &= ~0x8000;
        sg_light_ctrl_data.shade_param = SHADE_PARAM_COLOR;
        sg_light_ctrl_data.sg_usRemainTime100ms = DEFAULT_UINT_MAX * HANLD_CYCLE_MS / 100; 
    } else {
        sg_light_ctrl_data.shade_param = SHADE_PARAM_BRIGHT;
        sg_light_ctrl_data.sg_usRemainTime100ms = sg_light_ctrl_data.blinkTime100ms; 
    }
    TAL_PR_DEBUG("blink...C->%d, W->%d, timer->%d", data->white, data->warm, sg_light_ctrl_data.sg_usRemainTime100ms);
    cnt++;
}
/**
 * @description: The dimming end callback, usually because the dimming process receives a STOP command, or the dimming process ends
 * @param {type} none
 * @return: none
 */
STATIC VOID_T __app_light_ctrl_data_move_complete_cb(VOID_T)
{
    TAL_SEND_QOS_E QOS = QOS_VIP_0;
    UCHAR_T level = 0;
    USHORT_T temper = sg_light_ctrl_data.app_data.temper;

    level = app_light_tools_B_2_zig_b(sg_light_ctrl_data.app_data.bright);
    TAL_PR_DEBUG("Move Complete Callback...");

    tal_zg_write_attribute(1,
                           CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                           ATTR_CURRENT_LEVEL_ATTRIBUTE_ID,
                           &level,
                           ATTR_INT8U_ATTRIBUTE_TYPE);
    app_level_report_level_value(QOS, 100, level);
    tal_zg_write_attribute(1,
                           CLUSTER_COLOR_CONTROL_CLUSTER_ID,
                           ATTR_COLOR_CONTROL_COLOR_TEMPERATURE_ATTRIBUTE_ID,
                           &temper,
                           ATTR_INT8U_ATTRIBUTE_TYPE);
    app_color_report_temperature_value(QOS, 200, temper);
}
/**
 * @brief 
 * 
 * @param app_data 
 * @return UINT8_T 
 */
STATIC UINT8_T __app_light_ctrl_write_app_data_attr(APP_ALL_DATA_T *app_data)
{
    UCHAR_T buffer[sizeof(APP_ALL_DATA_T) + 1] = {0};
    if (app_data == NULL)
    {
        TAL_PR_DEBUG("app_data ERROR");
        return FALSE;
    }
    buffer[0] = sizeof(APP_ALL_DATA_T);
    tal_system_memcpy(&buffer[1], app_data, sizeof(APP_ALL_DATA_T));
    OPERATE_RET ret = tal_zg_write_attribute(1,
                                             CLUSTER_COLOR_CONTROL_CLUSTER_ID,
                                             TY_ATTR_LIGHT_ALL_DATA_ATTRIBUTE_ID,
                                             buffer,
                                             ATTR_ARRAY_ATTRIBUTE_TYPE);
    if (ret != OPRT_OK)
    {
        TAL_PR_DEBUG("Write app_data ERROR:%d", ret);
        return FALSE;
    }
    return TRUE;
}

VOID_T app_light_ctrl_more_rst_set(VOID_T)
{
    more_rst = TRUE;
}

/**
 * @note: set light switch data, adapte control data issued by system
 *          to control data format.
 * @param {IN UINT8_T on_off -> switch data, TRUE will turn on}
 * @return: OPERATE_RET -> OPRT_OK meaning need to call app_light_ctrl_proc() function!
 */
OPERATE_RET app_light_ctrl_data_switch_set(UINT8_T on_off)
{
    if (sg_light_ctrl_data.app_data.onoff != on_off)
    {
        sg_light_ctrl_data.app_data.onoff = (on_off == 0) ? FALSE : TRUE;
        return OPRT_OK;
    }
    return OPRT_COM_ERROR;
}

UINT16_T app_light_ctrl_data_switch_on_gradient(VOID_T)
{
    return sg_light_ctrl_data.on_gradient;
}

UINT16_T app_light_ctrl_data_switch_off_gradient(VOID_T)
{
    return sg_light_ctrl_data.off_gradient;
}

/**
 * @note: get light switch data
 * @param {OUT UINT8_T *onff -> switch data return}
 * @return: OPERATE_RET
 */
UINT8_T app_light_ctrl_data_switch_get(void)
{
    return sg_light_ctrl_data.app_data.onoff;
}

/**
 * @note: light switch data comparison
 * @param {OUT UINT8_T *onff -> switch data return}
 * @return: OPERATE_RET
 */
OPERATE_RET app_light_ctrl_data_switch_comparison(UINT8_T on_off)
{
    return ((on_off == sg_light_ctrl_data.app_data.onoff)) ? OPRT_COM_ERROR : OPRT_OK;
}

/**
 * @note: set light count down cnt
 * @param [in] {ULONG_T}count
 * @return: none
 */
VOID_T app_light_count_down_cnt_set(ULONG_T count)
{
    sg_light_ctrl_data.count_down_cnt = count;
}
/**
 * @note: return app_light_count_down_cnt_get
 * @param [in] none
 * @return: app_light_count_down_cnt_get
 */
ULONG_T app_light_count_down_cnt_get(VOID_T)
{
    return sg_light_ctrl_data.count_down_cnt;
}
/**
 * @note: set light mode data
 * @param {IN LIGHT_MODE_E Mode}
 * @attention:Mode value is below:
 *                                  WHITE_MODE = 0,
 *                                  COLOR_MODE = 1,
 *                                  SCENE_MODE = 2,
 *                                  MUSIC_MODE = 3,
 * @return: OPERATE_RET -> OPRT_OK meaning need to call app_light_ctrl_proc() function!
 */
OPERATE_RET app_light_ctrl_data_mode_set(LIGHT_MODE_E mode)
{
    LIGHT_MODE_E last_mode;
    last_mode = sg_light_ctrl_data.app_data.mode;
    if (mode > MODE_MAX)
    {
        TAL_PR_DEBUG("mode is illegal,set error");
        return OPRT_INVALID_PARM;
    }
    sg_light_ctrl_data.app_data.mode = mode;
    if (mode == last_mode)
    {
        //only scene mode can be set many time
        TAL_PR_DEBUG("the same mode set");
        return OPRT_INVALID_PARM;
    }
    return OPRT_OK;
}
/**
 * @note: set light bright data, adapte control data issued by system
 *          to control data format.
 * @param {IN USHORT_T bright}
 * @attention: acceptable range:10~1000
 * @attention: set bright value, will auto set the Mode to WHITE_MODE !
 * @return: OPERATE_RET -> OPRT_OK meaning need to call app_light_ctrl_proc() function!
 */
OPERATE_RET app_light_ctrl_data_bright_set(USHORT_T bright, USHORT_T transtime)
{
    USHORT_T last_bright = sg_light_ctrl_data.app_data.bright;

    if (bright < CTRL_CW_BRIGHT_VALUE_MIN)
    {
        TAL_PR_DEBUG("bright value is exceed range,set error");
        bright = CTRL_CW_BRIGHT_VALUE_MIN;
    }

    if (bright > CTRL_CW_BRIGHT_VALUE_MAX)
    {
        TAL_PR_DEBUG("bright value is exceed range,set error");
        bright = CTRL_CW_BRIGHT_VALUE_MAX;
    }

    
    sg_light_ctrl_data.app_data.bright = bright;
    if (sg_light_ctrl_data.app_data.mode)
    {
        sg_light_ctrl_data.app_data.mode = WHITE_MODE;
    }

    if (app_light_ctrl_data_switch_set(TRUE) == OPRT_OK)
    {
    
    } else if (bright == last_bright) {
        TAL_PR_DEBUG("the same bright set");
        return OPRT_COM_ERROR;
    } else{
        sg_light_ctrl_data.sg_usRemainTime100ms = transtime;
        sg_light_ctrl_data.shade_param = SHADE_PARAM_BRIGHT;
    }

    return OPRT_OK;
}

/**
 * @note: get light switch data
 * @param {OUT UINT8_T *onff -> switch data return}
 * @return: OPERATE_RET
 */
USHORT_T app_light_ctrl_handle_data_bright_get(VOID_T)
{
    LIGHT_DATA_T data = {0};
    app_light_ctrl_handle_data_get(&data);
    UINT16_T bright = data.white + data.warm;
    if (bright > CTRL_CW_BRIGHT_VALUE_MAX)
    {
        bright = CTRL_CW_BRIGHT_VALUE_MAX;
    }
    return bright;
}

/**
 * @note: set light temrperature data, adapte control data issued by system
 *          to control data format.
 * @param {IN USHORT_T temperature}
 * @attention: acceptable range:0~1000
 * @return: OPERATE_RET -> OPRT_OK meaning need to call app_light_ctrl_proc() function!
 */
OPERATE_RET app_light_ctrl_data_temp_set(USHORT_T temperature, USHORT_T transtime)
{
    USHORT_T last_temperature;

    last_temperature = sg_light_ctrl_data.app_data.temper;
    TAL_PR_DEBUG("sg_light_ctrl_data.temper =%d , temperature=%d, transtime=%d", sg_light_ctrl_data.app_data.temper, temperature, transtime);
    if (temperature > CTRL_CW_BRIGHT_VALUE_MAX)
    {
        TAL_PR_DEBUG("temperature value is exceed range,set error");
        sg_light_ctrl_data.app_data.temper = 0;
        return OPRT_INVALID_PARM;
    }

    sg_light_ctrl_data.app_data.temper = temperature;
    

    if (sg_light_ctrl_data.app_data.mode)
    {
        sg_light_ctrl_data.app_data.mode = WHITE_MODE;
    }

    if (app_light_ctrl_data_switch_set(TRUE) == OPRT_OK)
    {

    } else if (temperature == last_temperature) {
        TAL_PR_DEBUG("the same temperature set");
        return OPRT_COM_ERROR;
    } else {
        sg_light_ctrl_data.sg_usRemainTime100ms = transtime;
        sg_light_ctrl_data.shade_param = SHADE_PARAM_COLOR;
    }
    return OPRT_OK;
}

/**
 * 
 * @note: geta light bright data
 * @param {OUT USHORT_T *pBright -> bright data return}
 * @return: OOPERATE_RET
 */
OPERATE_RET app_light_ctrl_data_bright_get(USHORT_T *bright)
{

    *bright = sg_light_ctrl_data.app_data.bright;

    return OPRT_OK;
}

/**
 * @note: get light temrperature data
 * @param {OUT USHORT_T *pTemperature -> temperature data return}
 * @return: OPERATE_RET
 */
OPERATE_RET app_light_ctrl_data_temper_get(USHORT_T *temperature)
{
    *temperature = sg_light_ctrl_data.app_data.temper;

    return OPRT_OK;
}

/**
 * @note: get light mode data
 * @param {OUT LIGHT_MODE_E *Mode -> mode data return}
 * @return: OPERATE_RET
 */
LIGHT_MODE_E app_light_ctrl_data_mode_get(VOID)
{
    return sg_light_ctrl_data.app_data.mode;
}

OPERATE_RET app_light_ctrl_data_ty_scene_set(UINT8_T *data, UINT8_T data_len)
{
    if ((data_len < TY_SCENE_MIN_LENGTH) || \
        (data_len > TY_SCENE_MAX_LENGTH) || \
        ((data_len - TY_SCENE_HRAD_LENGTH) % TY_SCENEE_UNIT_LENGTH != 0))
    {
        TAL_PR_ERR("the ty scene data length err %d", data_len);
        return OPRT_INVALID_PARM;
    }
    
    if (app_light_ctrl_data_switch_set(TRUE) == OPRT_OK)
    {

    } else if ((sg_light_ctrl_data.app_data.ty_scene[0] == data_len) && \
        (tal_system_memcmp(&sg_light_ctrl_data.app_data.ty_scene[1], data, data_len) == 0)) {
        TAL_PR_NOTICE("the same ty_scene set");
        return OPRT_COM_ERROR;
    }
        
    if (TY_Scene_SetData(data, data_len) != OPRT_OK)
    {
        return OPRT_COM_ERROR;
    }

    sg_light_ctrl_data.app_data.ty_scene[0] = data_len;
    tal_system_memcpy(&sg_light_ctrl_data.app_data.ty_scene[1], data, data_len);
    
    return OPRT_OK;
}

UINT8_T *app_light_ctrl_data_ty_scene_get(VOID)
{
    return sg_light_ctrl_data.app_data.ty_scene;
}

OPERATE_RET app_light_ctrl_data_power_memory_set(UINT8_T *data, UINT8_T data_len)
{
    if (data_len != POWER_MEMORY_DATA_LENGTH)
    {
        TAL_PR_ERR("the power memory data length err %d", data_len);
        return OPRT_INVALID_PARM;
    }
    
    if ((sg_light_ctrl_data.app_data.power_memory[0] == data_len) && \
        (tal_system_memcmp(&sg_light_ctrl_data.app_data.power_memory[1], data, data_len) == 0)) {
        TAL_PR_NOTICE("the same power_memory set");
        return OPRT_COM_ERROR;
    }

    sg_light_ctrl_data.app_data.power_memory[0] = data_len;
    tal_system_memcpy(&sg_light_ctrl_data.app_data.power_memory[1], data, data_len);

    tal_sw_timer_start(etimer_save_data_delay, 5000, TAL_TIMER_ONCE);
    
    return OPRT_OK;
}

UINT8_T *app_light_ctrl_data_power_memory_get(VOID)
{
    return sg_light_ctrl_data.app_data.power_memory;
}

OPERATE_RET app_light_ctrl_data_do_not_disturb_set(UINT8_T no_disturb)
{   
    no_disturb = (no_disturb == FALSE) ? FALSE : TRUE;
    if (sg_light_ctrl_data.app_data.do_not_disturb == no_disturb) {
        TAL_PR_NOTICE("the same do_not_disturb set");
        return OPRT_COM_ERROR;
    }

    sg_light_ctrl_data.app_data.do_not_disturb = no_disturb;

    tal_sw_timer_start(etimer_save_data_delay, 5000, TAL_TIMER_ONCE);
    
    return OPRT_OK;
}

UINT8_T app_light_ctrl_data_do_not_disturb_get(VOID)
{
    return sg_light_ctrl_data.app_data.do_not_disturb;
}

OPERATE_RET app_light_ctrl_data_switch_gradient_set(UINT8_T *data, UINT8_T data_len)
{
    if (data_len != SWITCH_GRADIENT_DATA_LENGTH)
    {
        TAL_PR_ERR("the switch gradient data length err %d", data_len);
        return OPRT_INVALID_PARM;
    }
    
    if ((sg_light_ctrl_data.app_data.switch_gradient[0] == data_len) && \
        (tal_system_memcmp(&sg_light_ctrl_data.app_data.switch_gradient[1], data, data_len) == 0)) {
        TAL_PR_DEBUG("the same switch_gradient set");
        return OPRT_COM_ERROR;
    }

    sg_light_ctrl_data.app_data.switch_gradient[0] = data_len;
    tal_system_memcpy(&sg_light_ctrl_data.app_data.switch_gradient[1], data, data_len);
    
    sg_light_ctrl_data.on_gradient = sg_light_ctrl_data.app_data.switch_gradient[2];
    sg_light_ctrl_data.on_gradient = (sg_light_ctrl_data.on_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[3];
    sg_light_ctrl_data.on_gradient = (sg_light_ctrl_data.on_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[4];
    
    sg_light_ctrl_data.off_gradient = sg_light_ctrl_data.app_data.switch_gradient[5];
    sg_light_ctrl_data.off_gradient = (sg_light_ctrl_data.off_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[6];
    sg_light_ctrl_data.off_gradient = (sg_light_ctrl_data.off_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[7];

    tal_sw_timer_start(etimer_save_data_delay, 5000, TAL_TIMER_ONCE);
    
    return OPRT_OK;
}

UINT8_T *app_light_ctrl_data_switch_gradient_get(VOID)
{
    return sg_light_ctrl_data.app_data.switch_gradient;
}

/**
 * @description: start blink
 * @param {IN ULONG_T blink_time -> blink phase}
 * @attention: blink display will as the parm
 *             -- NetColor, usNetBright in configuration.
 * @return: none
 */
OPERATE_RET app_light_ctrl_blink_start(ULONG_T blink_time)
{
    sg_light_ctrl_data.nwk_flag = TRUE;
    sg_light_ctrl_data.blinkTime100ms = 0x8000 | (blink_time / 100);
    app_handle_start(FALSE);
    return OPRT_OK;
}

UINT8_T app_light_ctrl_nwk_flag_get(VOID)
{
    return sg_light_ctrl_data.nwk_flag;
}

/**
 * @brief 
 * 
 * @param timer_id 
 * @param arg 
 * @return VOID_T 
 */
VOID_T app_light_ctrl_mf_blink_cb(TIMER_ID timer_id, VOID_T *arg)
{
    STATIC UCHAR_T cnt = 0;
    LIGHT_DATA_T ctrl_data;

    STATIC USHORT_T sv_2min = 4 * 60 * 2; //2min

    TAL_PR_DEBUG("Run Into>>>%s", __FUNCTION__);
    tal_system_memset(&ctrl_data, 0, SIZEOF(LIGHT_DATA_T));
    ctrl_data.white = (cnt % 2) ? 1000 : 0;
    ctrl_data.warm = 0;
    TAL_PR_DEBUG("blink...C->%d, W->%d", ctrl_data.white, ctrl_data.warm);
    cnt++;
    
    app_pwm_cw_set(ctrl_data.white, ctrl_data.warm);

    sv_2min--;
    if (sv_2min)
    {
        tal_sw_timer_start(etimer_mf_blink, 250, TAL_TIMER_ONCE);
    }
    else
    {
        tal_sw_timer_delete(etimer_mf_blink);
    }
}
/**
 * @description: stop blink
 * @param {type} none
 * @attention: blink stop will directly go to normal status display
 *              normal status will bright as default bright parm
 *              -- usDefBright,usDefTemper,DefColor in configuration.
 * @return: none
 */
OPERATE_RET app_light_ctrl_blink_stop(VOID_T)
{
    sg_light_ctrl_data.nwk_flag = FALSE;
    sg_light_ctrl_data.sg_usRemainTime100ms = DEFAULT_UINT_MAX * HANLD_CYCLE_MS / 100;
    app_handle_start(FALSE);
    tal_sw_timer_start(etimer_join_end_sync, 10000, TAL_TIMER_ONCE);
    tal_sw_timer_start(etimer_save_data_delay, 10, TAL_TIMER_ONCE);
    return OPRT_OK;
}
/**
 * @brief get the light shade param object
 * 
 * @param shade_param 
 */
SHADE_PARAM_E app_light_ctrl_shade_param_get(VOID)
{
    return sg_light_ctrl_data.shade_param;
}

void app_light_ctrl_white_target_val(LIGHT_DATA_T *data)
{
    app_light_tools_BT2CW(sg_light_ctrl_data.app_data.bright, sg_light_ctrl_data.app_data.temper, data);
}

/**
 * @description: Get shade remain time
 * @param [OUT]{RemainTime100ms} remain time: 100ms
 * @return: none
 */
VOID_T app_light_ctrl_remain_time_ms_get(USHORT_T *remain_time_100ms)
{
    *remain_time_100ms = sg_light_ctrl_data.sg_usRemainTime100ms;
}

/**
 * @description: Get shade remain time
 * @param [OUT]{RemainTime100ms} remain time: 100ms
 * @return: none
 */
VOID_T app_light_ctrl_remain_time_ms_set(USHORT_T remain_time_100ms)
{
    sg_light_ctrl_data.sg_usRemainTime100ms = remain_time_100ms;
}

/**
 * @description: stop step level or move level
 * @param {type} none
 * @return: none
 */
OPERATE_RET app_light_ctrl_data_move_stop(VOID_T)
{
    tal_sw_timer_stop(etimer_light_shade);
    LIGHT_DATA_T data = {0};
    app_light_ctrl_handle_data_get(&data);

    app_light_tools_CW2BT(&data, &sg_light_ctrl_data.app_data.bright, &sg_light_ctrl_data.app_data.temper);

    TAL_PR_DEBUG(" in app_light_ctrl_data_move_stop ");

    __app_light_ctrl_data_move_complete_cb();
    return OPRT_OK;
}

/**
 * @brief 
 * 
 * @param timer_id 
 * @param arg 
 * @return STATIC 
 */
VOID_T app_light_ctrl_save_data_delay_cb(TIMER_ID timer_id, VOID_T *arg)
{
    TAL_PR_DEBUG("Run Into>>>%s", __FUNCTION__);

    TAL_KV_STATUS_T v_ret =TAL_KV_SUCC;
    v_ret=tal_kv_flash_write(KV_ID_APP_DATA, sizeof(APP_DATA_T),  (UCHAR_T *)&sg_light_ctrl_data.app_data);
    if (v_ret !=TAL_KV_SUCC){
        TAL_PR_DEBUG("update app data error.");
        return;
    }

    if (sg_light_ctrl_data.app_data.mode == WHITE_MODE)
    {
        APP_ALL_DATA_T v_dcb = {
            .onoff = sg_light_ctrl_data.app_data.onoff,
            .mode = sg_light_ctrl_data.app_data.mode,
            .B = sg_light_ctrl_data.app_data.bright,
            .T = sg_light_ctrl_data.app_data.temper,
        };
        __app_light_ctrl_write_app_data_attr(&v_dcb);
    }
}
/**
 * @brief 
 * 
 * @param timer_id 
 * @param arg 
 * @return VOID_T 
 */
VOID_T app_light_ctrl_power_on_sync_cb(TIMER_ID timer_id, VOID_T *arg)
{

    TAL_PR_DEBUG("Power On Sync Light State.");

    app_onoff_report_value(QOS_VIP_0, 0, sg_light_ctrl_data.app_data.onoff);
    app_color_report_mode_value(QOS_VIP_0, 100, sg_light_ctrl_data.app_data.mode);
    app_color_report_ty_temperature_value(QOS_VIP_0, 200, sg_light_ctrl_data.app_data.temper);
    app_level_report_ty_bright_value(QOS_VIP_0, 300, sg_light_ctrl_data.app_data.bright);
    app_color_report_ty_scene_value(QOS_VIP_0, 400, &sg_light_ctrl_data.app_data.ty_scene[1], sg_light_ctrl_data.app_data.ty_scene[0]);
    app_color_report_power_memory_value(QOS_VIP_0, 500, &sg_light_ctrl_data.app_data.power_memory[1], sg_light_ctrl_data.app_data.power_memory[0]);
    app_color_report_do_not_disturb_value(QOS_VIP_0, 600, sg_light_ctrl_data.app_data.do_not_disturb);
    app_color_report_switch_gradient_value(QOS_VIP_0, 700, &sg_light_ctrl_data.app_data.switch_gradient[1], sg_light_ctrl_data.app_data.switch_gradient[0]);
    app_onoff_report_count_down_data(QOS_1, 800, 0);
    //update on/off attribute
    tal_zg_write_attribute(1,
                           CLUSTER_ON_OFF_CLUSTER_ID,
                           ATTR_ON_OFF_ATTRIBUTE_ID,
                           &sg_light_ctrl_data.app_data.onoff,
                           ATTR_BOOLEAN_ATTRIBUTE_TYPE);
}

/**
 * @brief 
 * 
 * @param timer_id 
 * @param arg 
 * @return VOID_T 
 */
VOID_T app_light_ctrl_join_end_sync_cb(TIMER_ID timer_id, VOID_T *arg)
{

    TAL_PR_DEBUG("join end Sync Light State.");

    app_onoff_report_value(QOS_1, 0, sg_light_ctrl_data.app_data.onoff); ///<  report data need
    app_color_report_mode_value(QOS_1, 200, sg_light_ctrl_data.app_data.mode);

    app_color_report_ty_temperature_value(QOS_VIP_0, 400, sg_light_ctrl_data.app_data.temper);
    app_level_report_ty_bright_value(QOS_VIP_0, 600, sg_light_ctrl_data.app_data.bright);

    tal_zg_write_attribute(1,
                           CLUSTER_ON_OFF_CLUSTER_ID,
                           ATTR_ON_OFF_ATTRIBUTE_ID,
                           &sg_light_ctrl_data.app_data.onoff,
                           ATTR_BOOLEAN_ATTRIBUTE_TYPE);
}

#define TY_SCENE_DEF_LEN 18
STATIC const UCHAR_T ty_scene_def[TY_SCENE_DEF_LEN + 1] = {
    TY_SCENE_DEF_LEN,   /*长度：5 + 3 * 9 */
    0x31,   /*情景号*/
    0x03,
    0x46, 0x46, 0x00, 0x0c, 0x00, 0x00, 0xe8, 0x00,
    0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
};

#define RHYTHM_MODE_DEF_LEN 32
STATIC const UCHAR_T rhythm_mode_def[RHYTHM_MODE_DEF_LEN + 1] = {
    RHYTHM_MODE_DEF_LEN,   /*长度：5 + 3 * 9 */
    0x00,   /*版本号*/
    0x00,   /*开关：关*/
    0x00,   /*渐变模式：一直渐变*/
    0x7F,   /*日期设定：每天*/ 
    0x03,   /*节点个数：3个*/
    0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x64,
    0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x32,
    0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00,
};

STATIC const UCHAR_T power_memory_def[POWER_MEMORY_DATA_LENGTH + 1] = {
    POWER_MEMORY_DATA_LENGTH,   /*长度：12 */
    0x00,       /*版本号*/
    0x01,       /*模式：恢复记忆值*/
    0x00, 0xB4, /*色调：青色*/
    0x03, 0xE8, /*饱和度：最纯*/ 
    0x00, 0x00, /*彩光亮度：不亮*/
    0x01, 0xF4, /*白光亮度：最亮*/ 
    0x01, 0xF4, /*色温：50%色温*/
};

STATIC const UCHAR_T switch_gradient_def[SWITCH_GRADIENT_DATA_LENGTH + 1] = {
    SWITCH_GRADIENT_DATA_LENGTH,   /*长度：7 */
    0x00,   /*版本号*/
    0x00, 0x03, 0xE8, /*打开渐明时长：1S*/ 
    0x00, 0x03, 0xE8, /*关闭渐暗时长：1S*/
};

OPERATE_RET app_light_ctrl_data_reset(VOID_T)
{
    UCHAR_T resetCnt = 0;
    if (sg_light_ctrl_data.app_data.head != APP_DATA_FALSH_HEAD)
    {
        sg_light_ctrl_data.app_data.head = APP_DATA_FALSH_HEAD;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.onoff != TRUE)
    {
        sg_light_ctrl_data.app_data.onoff = TRUE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.mode != WHITE_MODE)
    {
        sg_light_ctrl_data.app_data.mode = WHITE_MODE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.bright != FACTORY_DEFAULT_BRIGHT_VALUE)
    {
        sg_light_ctrl_data.app_data.bright = FACTORY_DEFAULT_BRIGHT_VALUE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.temper != FACTORY_DEFAULT_TEMPER_VALUE)
    {
        sg_light_ctrl_data.app_data.temper = FACTORY_DEFAULT_TEMPER_VALUE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.do_not_disturb != FALSE)
    {
        sg_light_ctrl_data.app_data.do_not_disturb = FALSE;
        resetCnt++;
    }

    if (memcmp(&sg_light_ctrl_data.app_data.rhythm_mode, &rhythm_mode_def, RHYTHM_MODE_DEF_LEN + 1) != 0)
    {
        memcpy(&sg_light_ctrl_data.app_data.rhythm_mode, &rhythm_mode_def, RHYTHM_MODE_DEF_LEN + 1);
        resetCnt++;
    }

    if (memcmp(&sg_light_ctrl_data.app_data.power_memory, &power_memory_def, POWER_MEMORY_DATA_LENGTH + 1) != 0)
    {
        memcpy(&sg_light_ctrl_data.app_data.power_memory, &power_memory_def, POWER_MEMORY_DATA_LENGTH + 1);
        resetCnt++;
    }

    if (memcmp(&sg_light_ctrl_data.app_data.switch_gradient, &switch_gradient_def, SWITCH_GRADIENT_DATA_LENGTH + 1) != 0)
    {
        memcpy(&sg_light_ctrl_data.app_data.switch_gradient, &switch_gradient_def, SWITCH_GRADIENT_DATA_LENGTH + 1);
        resetCnt++;
    }

    if (memcmp(&sg_light_ctrl_data.app_data.ty_scene, &ty_scene_def, TY_SCENE_DEF_LEN + 1) != 0)
    {
        memcpy(&sg_light_ctrl_data.app_data.ty_scene, &ty_scene_def, TY_SCENE_DEF_LEN + 1);
        resetCnt++;
    }
    
    if (resetCnt == 0)
    {
        return OPRT_COM_ERROR;
    } else {
        //有变化
        return OPRT_OK;
    }
}

VOID app_light_ctrl_data_check(VOID_T)
{
    UCHAR_T resetCnt = 0;
    if (sg_light_ctrl_data.app_data.head != APP_DATA_FALSH_HEAD)
    {
        sg_light_ctrl_data.app_data.head = APP_DATA_FALSH_HEAD;
        resetCnt++;
    }

    if ((sg_light_ctrl_data.app_data.onoff != TRUE) && (sg_light_ctrl_data.app_data.onoff != FALSE))
    {
        sg_light_ctrl_data.app_data.onoff = TRUE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.mode > MODE_MAX)
    {
        sg_light_ctrl_data.app_data.mode = WHITE_MODE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.bright > CTRL_CAL_VALUE_RANGE)
    {
        sg_light_ctrl_data.app_data.bright = CTRL_CAL_VALUE_RANGE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.temper > CTRL_CAL_VALUE_RANGE)
    {
        sg_light_ctrl_data.app_data.temper = CTRL_CAL_VALUE_RANGE;
        resetCnt++;
    }

    if ((sg_light_ctrl_data.app_data.do_not_disturb != TRUE) && (sg_light_ctrl_data.app_data.do_not_disturb != FALSE))
    {
        sg_light_ctrl_data.app_data.do_not_disturb = FALSE;
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.rhythm_mode[0] > RHYTHM_MODE_MAX_LENGTH)
    {
        memcpy(&sg_light_ctrl_data.app_data.rhythm_mode, &rhythm_mode_def, RHYTHM_MODE_DEF_LEN + 1);
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.power_memory[0] != POWER_MEMORY_DATA_LENGTH)
    {
        memcpy(&sg_light_ctrl_data.app_data.power_memory, &power_memory_def, POWER_MEMORY_DATA_LENGTH + 1);
        resetCnt++;
    }

    if (sg_light_ctrl_data.app_data.switch_gradient[0] != SWITCH_GRADIENT_DATA_LENGTH)
    {
        memcpy(&sg_light_ctrl_data.app_data.switch_gradient, &switch_gradient_def, SWITCH_GRADIENT_DATA_LENGTH + 1);
        resetCnt++;
    }

    if ((sg_light_ctrl_data.app_data.ty_scene[0] < TY_SCENE_MIN_LENGTH) || \
        (sg_light_ctrl_data.app_data.ty_scene[0] > TY_SCENE_MAX_LENGTH) || \
        ((sg_light_ctrl_data.app_data.ty_scene[0] - TY_SCENE_HRAD_LENGTH) % TY_SCENEE_UNIT_LENGTH != 0))
    {
        memcpy(&sg_light_ctrl_data.app_data.ty_scene, &ty_scene_def, TY_SCENE_DEF_LEN + 1);
        resetCnt++;
    }

    sg_light_ctrl_data.on_gradient = sg_light_ctrl_data.app_data.switch_gradient[2];
    sg_light_ctrl_data.on_gradient = (sg_light_ctrl_data.on_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[3];
    sg_light_ctrl_data.on_gradient = (sg_light_ctrl_data.on_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[4];
    
    sg_light_ctrl_data.off_gradient = sg_light_ctrl_data.app_data.switch_gradient[5];
    sg_light_ctrl_data.off_gradient = (sg_light_ctrl_data.off_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[6];
    sg_light_ctrl_data.off_gradient = (sg_light_ctrl_data.off_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[7];

    if ((sg_light_ctrl_data.on_gradient < 100) || \
        (sg_light_ctrl_data.on_gradient > 60000) || \
        (sg_light_ctrl_data.off_gradient < 100) || \
        (sg_light_ctrl_data.off_gradient > 60000))
    {
        memcpy(&sg_light_ctrl_data.app_data.switch_gradient, &switch_gradient_def, SWITCH_GRADIENT_DATA_LENGTH + 1);
        sg_light_ctrl_data.on_gradient = sg_light_ctrl_data.app_data.switch_gradient[2];
        sg_light_ctrl_data.on_gradient = (sg_light_ctrl_data.on_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[3];
        sg_light_ctrl_data.on_gradient = (sg_light_ctrl_data.on_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[4];
        
        sg_light_ctrl_data.off_gradient = sg_light_ctrl_data.app_data.switch_gradient[5];
        sg_light_ctrl_data.off_gradient = (sg_light_ctrl_data.off_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[6];
        sg_light_ctrl_data.off_gradient = (sg_light_ctrl_data.off_gradient << 8) | sg_light_ctrl_data.app_data.switch_gradient[7];
    }

    if (sg_light_ctrl_data.app_data.power_memory[2] == 0x00) /*初始默认值*/
    {
        sg_light_ctrl_data.app_data.mode = WHITE_MODE;
        sg_light_ctrl_data.app_data.bright = CTRL_CAL_VALUE_RANGE;
        sg_light_ctrl_data.app_data.temper = CTRL_CAL_VALUE_RANGE;
    } else if (sg_light_ctrl_data.app_data.power_memory[2] == 0x02) /*用户定制*/{
        sg_light_ctrl_data.app_data.mode = WHITE_MODE;
        sg_light_ctrl_data.app_data.bright = (((USHORT_T)sg_light_ctrl_data.app_data.power_memory[9] << 8) & 0xff00) | (sg_light_ctrl_data.app_data.power_memory[10] & 0xff);    //提取亮度的值
        sg_light_ctrl_data.app_data.temper = (((USHORT_T)sg_light_ctrl_data.app_data.power_memory[11] << 8) & 0xff00) | (sg_light_ctrl_data.app_data.power_memory[12] & 0xff);    //提取色温的值
    }
    
    if (sg_light_ctrl_data.app_data.do_not_disturb == TRUE)
    {
        if ((sg_light_ctrl_data.app_data.onoff == FALSE) && (more_rst == TRUE))
        {
            sg_light_ctrl_data.app_data.onoff = TRUE;
        }
    } else {
        sg_light_ctrl_data.app_data.onoff = TRUE;
    }
	sg_light_ctrl_data.shade_param = SHADE_PARAM_BRIGHT;
    sg_light_ctrl_data.nwk_flag = FALSE;
    sg_light_ctrl_data.count_down_cnt = 0;
    sg_light_ctrl_data.sg_usRemainTime100ms = sg_light_ctrl_data.on_gradient / 100;
    if (resetCnt != 0)
    {
        tal_sw_timer_start(etimer_save_data_delay, 2000, TAL_TIMER_ONCE);
    }
}

/**
 * @brief: 灯控制初始化接口
 * @param[in]: config_data 初始化的数据指针 
 * @attention: this function need apply tuya_light_sys_hardware_reboot_judge();
 * @return: OPERATE_LIGHT 0表示成功, 非0请参照tuya error code描述文档
 */
OPERATE_RET app_light_ctrl_init(void)
{
    ZG_NWK_TYPE_E nwk_type = tkl_zg_nwk_type_get();
    TAL_PR_DEBUG("nwk_type =%d", nwk_type);

    TAL_KV_STATUS_T v_ret =TAL_KV_SUCC;

    tal_system_memset(&sg_light_ctrl_data, 0, SIZEOF(LIGHT_CTRL_DATA_T));

    v_ret=tal_kv_flash_read(KV_ID_APP_DATA, sizeof(APP_DATA_T),  (UCHAR_T *)&sg_light_ctrl_data.app_data);
    if ((v_ret !=TAL_KV_SUCC) || (sg_light_ctrl_data.app_data.head != APP_DATA_FALSH_HEAD))
    {
        OPERATE_RET ret = app_light_ctrl_data_reset();
        if (ret == OPRT_OK)
        {
            tal_sw_timer_start(etimer_save_data_delay, 2000, TAL_TIMER_ONCE);
        }
    }
    app_light_ctrl_data_check();
    TY_Scene_SetData(&sg_light_ctrl_data.app_data.ty_scene[1], sg_light_ctrl_data.app_data.ty_scene[0]);
    ULONG_T delay_ms = 0;
    delay_ms = 5000 + tal_system_get_random(5000);
    tal_sw_timer_start(etimer_power_on_sync, delay_ms, TAL_TIMER_ONCE);
    return OPRT_OK;
}

